#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>               
#include "load_file.h"         



/*a declatation block for the function headers */

int test(char *rule, char *dict, char *pswd);
int check_pswd(char *rule,char *pswd, char *dict);
int testrule(char *password, char *flags, char *dict);
int testkchars(char *password,int min,int max,char *flags, char *dict);
int testkdigits(char *password,int min,int max,char *flags, char *dict);
int testkletters(char *password,int min,int max,char *flags, char *dict);
int testkalpha(char *password,int min,int max,char *flags, char *dict);
int testupcase(char *password,char *flags, char *dictoriginal);
int testlowcase(char *password,char *flags, char *dictoriginal);
int testnocase(char *password,char *flags, char *dictoriginal);


int main(){

    char filename[80];
    int counter = 0;
    char rule[80];
    char pswd[80];
    char *dict;

    printf("Enter name of file that holds the dictionary:\n");
    scanf("%s", filename);
    dict = load_file(filename);
    if (dict == NULL) {
	printf("error reading dictionary file\n");
	return 0;
    }

    printf("Enter rule defining easy to guess passwords:\n");
    scanf("%s", rule);

    while (counter < 3){
        counter++;
        printf("Please enter a password:\n");
        scanf("%s", pswd);
	if (pswd == NULL) {
	printf("NULL password, try again\n");
	continue;
	}
        if (check_pswd(rule, pswd, dict)) {
            printf("Secure password!\n");
            return 1;
        }
        else printf("Easy to guess password, try again\n");
    }
    free(dict);
    return 0;
}


/*testrule is invoked from check_pswd, it operates as a dispatcher in the sense that it checks the next char
	and calls the next appropriate function with the right params.
	the function returns 1 to check_pswd iff the password is easy to guess*/

int testrule(char *password, char *flags, char *dict)
{
    int min, max;

    if ((*flags == '\0') && (*password == '\0'))
	{
        return 1;
	}
    else
    {
        if (*flags == '?')
        {
        min = atoi((flags+= 1));
	while (*flags != '-')
		flags=flags+1;
        max = atoi((flags+= 1));
	while (('0' <= *flags) && (*flags <= '9'))
		flags=flags+1;
        return testkchars(password,min,max,flags,dict);
       } else
       if (*flags == '&')
       {
        min = atoi((flags+= 1));
	while (*flags != '-')
		flags=flags+1;
        max = atoi((flags+= 1));
	while (('0' <= *flags) && (*flags <= '9'))
		flags=flags+1;
               return testkdigits(password,min,max,flags,dict);
       } else
          if (*flags == '!')
       {
        min = atoi((flags+= 1));
	while (*flags != '-')
		flags=flags+1;
        max = atoi((flags+= 1));
	while (('0' <= *flags) && (*flags <= '9'))
		flags=flags+1;
        return testkletters(password,min,max,flags,dict);
       } else
       if (*flags == '%')
       {
        min = atoi((flags+= 1));
	while (*flags != '-')
		flags=flags+1;
        max = atoi((flags+= 1));
	while (('0' <= *flags) && (*flags <= '9'))
		flags=flags+1;
               return testkalpha(password,min,max,flags,dict);
       } else
          if (*flags == '@')
    {
               flags+= 1;
               return testlowcase(password,flags,dict);
       } else
       if (*flags == '#')
        {
               flags+= 1;
               return testupcase(password,flags,dict);
       } else
       if (*flags == '$')
        {
               flags+= 1;
            return testnocase(password,flags,dict);    /*this is the case -insensitive function*/
       } else
       {
           return 0;
       }
    }
}


/*teskchars,testkdigits,testkalpha,testkletters are all the same function with different booleans
		that fit the specific checks needed from them
		they work recursivley with a call back to testrule
		they all return 1 iff the password is easy to guess*/

int testkchars(char *password,int min,int max,char *flags, char *dict)
{
    int i;
    for(i=0 ; i<min ; ++i)
    {
        if (*password == '\0')
            return 0;
        else
            ++password;
    }

    for (i=min ; i<=max ; ++i)
    {
        if (testrule(password,flags,dict)==1)
            return 1;
        else
        {
            if (*password == '\0')
                return 0;
            else
                ++password;
        }

    }
    return 0;
}





int testkdigits(char *password,int min,int max,char *flags, char *dict)
{
    int i;
    for(i=0 ; i<min ; ++i)
    {
        if ((*password == '\0') || (*password < '0') || (*password > '9'))
            return 0;
        else
            ++password;
    }

    for (i=min ; i<=max ; ++i)
    {
        if (testrule(password,flags,dict)==1)
            return 1;
        else
        {
            if ((*password == '\0') || (*password < '0') || (*password > '9'))
                return 0;
            else
                ++password;
        }

    }
    return 0;
}


int testkletters(char *password,int min,int max,char *flags, char *dict)
{
    int i;
    for(i=0 ; i<min ; ++i)
    {
        if ((*password == '\0') || (*password < 'A') || (*password >'z') || ((*password > 'Z') && (*password < 'a')))
            return 0;
        else
            ++password;
    }

    for (i=min ; i<=max ; ++i)
    {
        if (testrule(password,flags,dict)==1)
            return 1;
        else
        {
            if ((*password == '\0') || (*password < 'A') || (*password> 'z') || ((*password > 'Z') && (*password < 'a')))
                return 0;
            else
                ++password;
        }

    }
    return 0;
}



int testkalpha(char *password,int min,int max,char *flags, char *dict)
{
    int i;
    for(i=0 ; i<min ; ++i)
    {
        if ((*password == '\0') || (*password < '0') || (*password >'z') || ((*password > '9') && (*password < 'A')) || ((*password > 'Z')
			&& (*password < 'a')))
            return 0;
        else
            ++password;
    }

    for (i=min ; i<=max ; ++i)
    {
        if (testrule(password,flags,dict)==1)
            return 1;
        else
        {
            if ((*password == '\0') || (*password < '0') || (*password> 'z') || ((*password > '9') && (*password < 'A')) || ((*password >
			'Z') && (*password < 'a')))
                return 0;
            else
                ++password;
        }

    }
    return 0;
}


/*testupcase, testlowcase and testnocase are the upper-case,lower-case and case-insensitive dictionary word checking function
	they call testrule recursivly and return 1 iff the password is easy to guess */


int testlowcase(char *password,char *flags, char *dictoriginal)
{
    char *passiter = password;
    char *dict = dictoriginal;
    int match = 0; /* a potential match to a word*/

        while (*dict != '\0')
        {
            match = 2;                          /*not found (1) and not a miss (0)*/
            passiter = password;
            while ((*dict != '\0') && (*dict != '\n') && (*dict != '\r') && (*dict != '\t') && (match!=0) &&
			(passiter!='\0'))
            {
                if (*passiter == tolower(*dict))  
                {
                    ++passiter;
                    ++dict;
                    match = 1;
                } else
                    match = 0;
            }
            if (match == 0)
                while ((*dict != '\0') && (*dict != '\n') && (*dict != '\r') && (*dict != '\t'))
                    ++dict;
            else
                if (testrule(passiter,flags,dictoriginal)==1)
                    return 1;
            if (*dict != '\0')
                ++dict;
        }

        return 0;
}





int testupcase(char *password,char *flags, char *dictoriginal)
{
    char *passiter = password;
    char *dict = dictoriginal;
    int match = 0; /* a potential match to a word*/


        while (*dict != '\0')
        {
            match = 2;                          /*not found (1) and not a miss (0)*/
            passiter = password;
            while ((*dict != '\0') && (*dict != '\n') && (*dict != '\r') && (*dict != '\t') && (match!=0) &&
			(passiter!='\0'))
            {
                if (*passiter == toupper(*dict))  
                {
                    ++passiter;
                    ++dict;
                    match = 1;
                } else
                    match = 0;
            }
            if (match == 0)
                while ((*dict != '\0') && (*dict != '\n') && (*dict != '\r') && (*dict != '\t'))
                    ++dict;
            else
                if (testrule(passiter,flags,dictoriginal)==1)
                    return 1;
            if (*dict != '\0')
                ++dict;
        }

        return 0;
}




int testnocase(char *password,char *flags, char *dictoriginal)
{
    char *passiter = password;
    char *dict = dictoriginal;
    int match = 0; /* a potential match to a word*/
   
        while (*dict != '\0')
        {
            match = 2;                          /*not found (1) and not a miss (0)*/
            passiter = password;
            while ((*dict != '\0') && (*dict != '\n') && (*dict != '\r') && (*dict != '\t') && (match!=0) &&
			(passiter!='\0'))
            {
                if (toupper(*passiter) == toupper(*dict))  
                {
                    ++passiter;
                    ++dict;
                    match = 1;
                } else
                    match = 0;
            }
            if (match == 0)
                while ((*dict != '\0') && (*dict != '\n') && (*dict != '\r') && (*dict != '\t'))
                    ++dict;
            else
                if (testrule(passiter,flags,dictoriginal)==1)
                    return 1;
            if (*dict != '\0')
                ++dict;
        }

        return 0;

    }

}



/*this is an iterative function that takes the rule and tests the input pswd against each block of the rule
	where a block is [**]'s  in:    [**] | [**] |...| [**] 
	most of this function is playing around with the chars. of the rule string (changing back and forth '|' to '\0')  */

int check_pswd(char *rule,char *pswd, char *dict){

    int flag = 0;
    char *head,*tail;   /*a tail ptr. helps us run along the rule string*/
    head = rule;
    tail = rule;

    while (!flag)
    {  /*the flag is raised at the end of the rule string*/
        while ((*tail != '|') && (*tail != '\0'))
            ++tail;

        if (*tail == '\0')   /*reached the end of the rule - raise the flag and check the block (last iteration)*/
        {
            flag = 1;
            if (!testrule(pswd,head,dict)) return 1;
        }
        else
        /*change the '|' to \0 and send head as a string to test_block()
            then advance tail and head by one for the next iteration*/
        {
            *tail = '\0';
            if (testrule(pswd,head,dict)) {
		*tail = '|';
		return 0;
	    }
		
            else {
		*tail = '|';
		head = tail+1;
	        tail = head;
	    }
        }
    }

    return 0;   /*if we reached this point it means that the checks were not good, we return 0*/
}
